﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http.Formatting;
using System.Json;
using System.IO;
using System.Diagnostics;

namespace Niknak.Social.Facebook
{
    /// <summary>
    /// Provides basic methods to retrieve likes from Facebook
    /// </summary>
    public static class Likes
    {
        public enum CursorType
        {
            Before,
            After
        }

        private const int DEFAULTPAGESIZE = 25;
        private const string GRAPHMETHODURI = "https://graph.facebook.com/{0}/likes?limit={1}&locale=en_GB{2}";

        /// <summary>
        /// Gets a list of users who've liked the specified post, wrapped by a container object with additional information about the request
        /// </summary>
        /// <param name="forPostId">ID of the post to get liked for</param>
        public static LikesData GetPostLikes(string forPostId)
        {
            return GetPostLikes(forPostId, DEFAULTPAGESIZE, null, CursorType.Before);
        }

        /// <summary>
        /// Gets a list of users who've liked the specified post, wrapped by a container object with additional information about the request
        /// </summary>
        /// <param name="forPostId">ID of the post to get liked for</param>
        /// <param name="pageSize">Limit the response to the specified page size</param>
        public static LikesData GetPostLikes(string forPostId, int pageSize)
        {
            return GetPostLikes(forPostId, pageSize, null, CursorType.Before);
        }

        /// <summary>
        /// Gets a list of users who've liked the specified post, wrapped by a container object with additional information about the request
        /// </summary>
        /// <param name="forPostId">ID of the post to get liked for</param>
        /// <param name="pageSize">Limit the response to the specified page size</param>
        /// <param name="cursor">Use the specified cursor ID from a previous page-limited request</param>
        /// <param name="cursorType">The type of cursor specified (before or after)</param>
        public static LikesData GetPostLikes(string forPostId, int pageSize, string cursor, CursorType cursorType)
        {
            LikesData returnData = new LikesData()
            { 
                Users = new Dictionary<string,string>(),
                StartOfStream = true,
                EndOfStream = true 
            };

            using (HttpClient client = new HttpClient())
            {
                string requestUrl = string.Format(
                    GRAPHMETHODURI,
                    forPostId,
                    pageSize,
                    !string.IsNullOrEmpty(cursor) ? cursorType == CursorType.Before ? "&before=" + cursor : "&after=" + cursor : ""
                );

                Debug.WriteLine("FB Likes requestUrl: " + requestUrl);

                Task retrieveLikes = client.GetAsync(requestUrl).ContinueWith(requestTask =>
                {
                    //get HTTP response from completed task
                    HttpResponseMessage response = requestTask.Result;

                    //check that response was successful or throw exception
                    response.EnsureSuccessStatusCode();

                    //necessary when retrieving more than about 50 likes at a time
                    MediaTypeFormatter.SkipStreamLimitChecks = true;

                    using (Stream streamResult = response.Content.ReadAsStreamAsync().Result)
                    {
                        JsonObject jsonResult = (JsonObject)JsonObject.Load(streamResult);

                        //likes
                        if (jsonResult.ContainsKey("data"))
                            foreach (JsonObject user in jsonResult["data"].ReadAs<JsonArray>())
                                returnData.Users.Add(user["id"].ReadAs<string>(), user["name"].ReadAs<string>());

                        //paging
                        if (jsonResult.ContainsKey("paging"))
                        {
                            if (jsonResult["paging"].ContainsKey("cursors"))
                            {
                                returnData.BeforeCursor = jsonResult["paging"]["cursors"]["before"].ReadAs<string>();
                                returnData.AfterCursor = jsonResult["paging"]["cursors"]["after"].ReadAs<string>();
                            }

                            if (jsonResult["paging"].ContainsKey("previous"))
                                returnData.StartOfStream = false;
                            if (jsonResult["paging"].ContainsKey("next"))
                                returnData.EndOfStream = false;
                        }
                    }
                });

                Task.WaitAll(retrieveLikes);
            }

            return returnData;
        }

        /// <summary>
        /// Container for Likes data
        /// </summary>
        public struct LikesData
        {
            /// <summary>
            /// Users who like the specified object
            /// </summary>
            public Dictionary<string, string> Users;
            /// <summary>
            /// Cursor for current results page
            /// </summary>
            public string BeforeCursor;
            /// <summary>
            /// Cursor for next results page
            /// </summary>
            public string AfterCursor;
            /// <summary>
            /// True when there are is no previous page
            /// </summary>
            public bool StartOfStream;
            /// <summary>
            /// True when there are is no next page
            /// </summary>
            public bool EndOfStream;
        }
    }
}
